#include <stdio.h>
#include <Windows.h>
#include<TlHelp32.h>

DWORD getPIDByProcessName(char *szProcessName) {
    STARTUPINFO st;
    PROCESS_INFORMATION pi;
    PROCESSENTRY32 ps;
    HANDLE hSnapshot;
    ZeroMemory(&st, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    st.cb = sizeof(STARTUPINFO);
    ZeroMemory(&ps, sizeof(PROCESSENTRY32));
    ps.dwSize = sizeof(PROCESSENTRY32);
    //拍摄进程快照
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    //快照拍摄失败
    if (hSnapshot == INVALID_HANDLE_VALUE) return 0;

    //快照中是否含有进程
    if (!Process32First(hSnapshot, &ps)) return 0;

    do {
        //遍历进程快照，比较进程名称
        if (strcmp(ps.szExeFile, szProcessName) == 0) {
            printf("%d %s %s \r\n", ps.th32ProcessID, ps.szExeFile, szProcessName);
            //将自身进程id放到容器中
            return ps.th32ProcessID;
        }
    } while (Process32Next(hSnapshot, &ps));

    //关闭快照句柄
    CloseHandle(hSnapshot);

    return 0;
}


char dllFileName[] = "D:/c_code/csdll/cmake-build-debug/libcsdll.dll";
char dllShortName[] = "libcsdll.dll";
void findDll(DWORD processId);

void freeLib(DWORD processId, HANDLE hProcess);

void loadLib(FARPROC loadlibrary, HANDLE hProcess);

int main(void) {
    int isUnicode = 0;
    #ifdef _UNICODE
        isUnicode = 1;
    #endif



    DWORD processId = getPIDByProcessName("cstrike.exe");

    HMODULE kernel = GetModuleHandleA("kernel32.dll");
    //此处应该使用 LoadLibraryA 或 LoadLibraryW，不能直接使用 LoadLibrary 因为LoadLibrary 是一个宏，在代码运行时不能直接使用
    FARPROC loadlibrary = NULL;
    if(isUnicode == 1){
        loadlibrary =  GetProcAddress(kernel, "LoadLibraryW");
    } else{
        loadlibrary =  GetProcAddress(kernel, "LoadLibraryA");
    }
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, (WINBOOL) NULL, processId);
    if (hProcess == NULL) {
        printf("open procee fail \r\n");
        return 1;
    }

    loadLib(loadlibrary, hProcess);

    findDll(processId);

    system("pause");

    printf("---------------------------------------------\r\n");

    freeLib(processId, hProcess);

    findDll(processId);

    // 关闭进程
    CloseHandle(hProcess);

    return 0;
}

void loadLib(FARPROC loadlibrary, HANDLE hProcess) {
    LPVOID lpparameter = VirtualAllocEx(hProcess, NULL, sizeof(dllFileName), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    //将目标dll的名字写入到目标进程
    WriteProcessMemory(hProcess, lpparameter, dllFileName, sizeof(dllFileName), NULL);
    //创建远程线程
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, (SIZE_T) NULL, (LPTHREAD_START_ROUTINE)loadlibrary, lpparameter, 0, NULL);
    //等待远程线程运行结束
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
}

void freeLib(DWORD processId,HANDLE hProcess) {
    MODULEENTRY32 me = {sizeof(me) }; //定义一个用于储存模块快照的结构体
    HANDLE                  hSnapshot = INVALID_HANDLE_VALUE;
    BOOL bMore = FALSE;
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
    //此函数检索与进程相关联的第一个模块的信息
    bMore = Module32First(hSnapshot, &me);
    for (; bMore; bMore = Module32Next(hSnapshot, &me))   //bMore用于判断该进程的模块快照是否还有，bFound用于判断是否找到了我们想要卸载的dll模块
    {
        if (lstrcmpi(me.szModule, dllShortName) == 0){
            printf("find free module %s \r\n",me.szModule);
            break;
        }
    }
    //得到 kernel32 的模块句柄，因为 loadlibrary 在该dll中
    HMODULE kernel = GetModuleHandleA("kernel32.dll");
    //此处应该使用 freeLibrary
    FARPROC freeLibrary = GetProcAddress(kernel, "FreeLibrary");
    // 使目标进程调用FreeLibrary，卸载DLL
    HANDLE hd1 = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)freeLibrary, me.modBaseAddr, 0, 0);
    // 等待线程执行完成
    WaitForSingleObject(hd1, 0xFFFFFFFF);
    DWORD bl2 = TRUE;
    GetExitCodeThread(hd1, &bl2);
}

void findDll(DWORD processId) {
    MODULEENTRY32 me = {sizeof(me)}; //定义一个用于储存模块快照的结构体
    HANDLE hSnapshot = INVALID_HANDLE_VALUE;
    BOOL bMore = FALSE;
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
    //此函数检索与进程相关联的第一个模块的信息
    bMore = Module32First(hSnapshot, &me);
    for (; bMore; bMore = Module32Next(hSnapshot, &me))   //bMore用于判断该进程的模块快照是否还有，bFound用于判断是否找到了我们想要卸载的dll模块
    {
        if(strcmp(me.szModule,dllShortName) == 0){
           printf("find module %s \r\n", me.szModule);
        }
    }
}
